之前和小伙伴完成的一个公众号,记录一下大概的开发过程吧
微信授权部分
我们用的是企业微信号,因为当时企业微信的官方文档更新不够及时,有部分 api 的请求参数是错误的,所以耽误了不少时间。
第一步
请求微信的api首先需要拿到一个 accessToken,而这个 token 是通过 corpid 和 corpsecret 来拿到的
corpid:每个企业都拥有唯一的 corpid,获取此信息可在管理后台“我的企业”-“企业信息”下查看(需要有管理员权限)
userid: 每个成员都有唯一的 userid,即所谓“帐号”。在管理后台->“通讯录”->点进某个成员的详情页,可以看到
这个 token 的过期时间默认是7200s,保存 token 的方法有很多种,比如说使用 redis,或者直接放在文件中,我这使用的是 guard_dog
1
| npm install guard_dog --save
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const guard_dog = require('guard_dog') guard_dog.init('ACCESS_TOKEN', (handler) => { request.get({ uri: 'https://qyapi.weixin.qq.com/cgi-bin/gettoken?', json: true, qs: { 'corpid': config.corpId, 'corpsecret': config.corpSecret } }, (err, res, body) => { if (err) { console.log(err) return } if (body.errcode) { return } handler(body.access_token, body.expires_in) }) })
|
第二步
配置可信域名,具体参考官方文档
配置完可信域名后,需要构造如下的连接来获得 code 参数
https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&agentid=AGENTID&state=STATE#wechat_redirect
用户点击后,页面将跳转至 redirect_uri?code=CODE&state=STATE
第三步
获得 code 参数后,就可以根据 code 获取用户信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| guard_dog.get('ACCESS_TOKEN', (data) => { const tokenParams = { 'access_token': data, 'code': code } let tokenUrl = 'https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?' + qs.stringify(tokenParams) request(tokenUrl, function (err, response, body) { let userTicket = body.split('"')[17] const userParams = { 'access_token': data } let userUrl = 'https://qyapi.weixin.qq.com/cgi-bin/user/getuserdetail?' + qs.stringify(userParams) let options = { url: userUrl, method: "POST", json: true, headers: { "content-type": "application/json", }, body: { "user_ticket": userTicket } } request(options, function (err, response, bodyData) { getBody(bodyData) .then(user => { res.json({ status: 200, user }) }) }) }) })
|
实时聊天部分
主要使用了 socket 来实现
1
| npm install socket.io --save
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const socket = require('socket.io') const server = http.createServer(app).listen(4000) const io = socket(server) io.on('connection', function (socket) { console.log('connected') socket.on('joinRoom', function (room) { socket.join(room) socket.on('sendData', function (data) { io.sockets.in(room).emit('receiveData', data) }) }) })
|
基本会议功能
如 加入会议/创建会议等等..
大部分都是一些对数据库的增删改查,创建会议时会自动分配一个五位数的随机会议号
1 2 3 4 5 6 7
| function radomNumber () { let number = '' for (let i = 0; i < 5; i++) { number += Math.floor(Math.random() * 10) } return number }
|
在这里,因为涉及到与数据库的交互,所以要注意异步问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| async number(req, res) { let result = '' let number = radomNumber() while (1) { result = await Conference.find({number: number}) if (JSON.stringify(result) === '[]') { res.json({ number }) return } else { number = radomNumber() } } }
|
有一个扫描二维码可以进入会议的功能,实现的逻辑就是通过 url 生成一个二维码返回给前端,用到了 qrcode 这个 npm 包
1
| npm install qrcode --save
|
1 2 3 4 5 6 7 8 9 10
| const QRCode = require('qrcode') getQRCode(req, res) { QRCode.toDataURL(req.body.url, function (err, url) { res.json({ status: 200, result: url }) }) }
|
活动部分功能
主要实现了四个活动功能,投票,抽奖,评分,红包
投票功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Vote .update({_id: req.body._id, "options.name": req.body.name}, {$inc:{"options.$.people":1}}) .then(() => { res.json({ status: 200, result: 'Success!' }) }) .catch(err => { console.error(err) if (err.json) { err.message = err.json.message } if (!returned) { res.status(500).json({ status: 500, result: err.message }) } })
|
抽奖功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Conference .findOne({number: req.body.number}) .then(conference => { let winner = Math.floor(Math.random() * (conference.people)) Lottery .update({_id: req.body._id}, {$set:{luckyDog: conference.userList[winner].name, isBegin: true}}) .then(() => { res.json({ status: 200, result: 'Success!' }) }) })
|
评分功能
1 2 3 4 5 6 7 8 9 10
| Rate .update( {_id:req.body._id}, {$inc: {totalScore: req.body.score, totalPeople: 1}}) .then(() => { res.json({ status: 200, result: 'Success!' }) })
|
红包功能
因为涉及到支付功能的话需要进行企业认证,所以采用了模拟红包的方法来实现
首先是一个红包算法
1 2 3 4 5 6 7 8 9 10 11 12
| function red (rpAmount, ppCount) { let rpResult=[] let rpRnds = [] let rpRndSum = 0 for(let i=0;i<ppCount;i++){ let rnd = Math.random(); rpRndSum += rnd; rpRnds.push(rnd); } rpRnds.forEach((rnd)=>{rpResult.push(parseFloat((rpAmount*rnd/rpRndSum).toFixed(2)))}) return rpResult }
|
rpAmount 是总金额,ppCount 是红包个数
以10个红包,一百元为例:
先生成10个0-1之间的随机数,加入到 rpRnds 这个数组中,然后对这个数组中的每个数都进行运算:rpAmount*rnd/rpRndSum
(rpRndSum 为这十个数的总和)
这样就可以得到较为随机的红包金额
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| Red .findOne({_id: req.body._id}) .then(red => { if (red.index > red.people - 1) { res.json({ money: null }) } else { Red .update({_id: req.body._id}, {$push:{luckyDogs: {username:req.body.username, money:red.money[red.index]}}, $inc:{index: 1}}) .then(() => { res.json({ money: red.money[red.index], index: red.index }) }) } })
|